﻿
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Threading;
using Microsoft.Extensions.Logging;
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
using YF_Music.Core;
using YF_Music.Core.Enums;
using YF_Music.Core.Events;
using YF_Music.Core.Mvvm;
using YF_Music.Core.Services;
using YF_Music.Modules.APP_My.Events;
using YF_Music.Modules.APP_My.Models;

namespace YF_Music.Modules.APP_My.ViewModels
{
  public class PlayMusicViewModel : RegionViewModelBase
  {
    public DelegateCommand PlayCommand { get; }
    public AsyncDelegateCommand PlayNextCommand { get; }
    public AsyncDelegateCommand PlayPreviousCommand { get; }
    private readonly PublishCoreEvent publishCoreEvent;
    private readonly IMusicPlayerService musicPlayerService;
    private readonly IHttpService http;
    private readonly IEventAggregator eventAggregator;
    private readonly ILogger<PlayMusicViewModel> logger;

    public IMusicPlayerService MusicPlayerService => musicPlayerService;
    public PlayMusicViewModel(IRegionManager regionManager, PublishCoreEvent publishCoreEvent, IMusicPlayerService musicPlayerService, IHttpService http, IEventAggregator eventAggregator, ILogger<PlayMusicViewModel> logger) : base(regionManager)
    {
      this.publishCoreEvent = publishCoreEvent;
      this.musicPlayerService = musicPlayerService;
      this.http = http;
      this.eventAggregator = eventAggregator;
      this.logger = logger;
      CancelCommand = new DelegateCommand(Cancel);
      SeekCommand = new DelegateCommand<double?>(MusicProgressChanged);
      PlayCommand = new DelegateCommand(PlayMusic);
      PlayNextCommand = new AsyncDelegateCommand(PlayNextAsync);
      PlayPreviousCommand = new AsyncDelegateCommand(PlayPreviousAsync);
      DisplayTime = musicPlayerService.CurrentPosition;
      timer = new()
      {
        Interval = TimeSpan.FromMilliseconds(1000),
      };
      timer.Tick += Timer_Tick;
      timer.Start();

      updateLyricTimer = new DispatcherTimer
      {
        Interval = TimeSpan.FromMilliseconds(50),
      };
      updateLyricTimer.Tick += UpdateLyricTimer_Tick;
      musicPlayerService.CurrentMusicChange += MusicPlayerService_CurrentMusicChange;
    }

    private async void MusicPlayerService_CurrentMusicChange(Core.Models.MusicItem obj)
    {
      StopUpdateLyricTimer();
      await UpdateLyric();
      StartUpdateLyricTimer();
    }

    private async Task PlayPreviousAsync()
    {
      StopUpdateLyricTimer();
      musicPlayerService.PlayPrevious();
      logger.LogInformation("Play previous music: {MusicTitle}", musicPlayerService.CurrentMusic?.Title);
      await Task.Delay(1);
    }
    private readonly string ServeUrl = "http://117.72.80.184:3000";
    private List<LrcLine> lyrics;
    /// <summary>
    /// 当前音乐的歌词
    /// </summary>
    public List<LrcLine> Lyrics
    {
      get { return lyrics; }
      set { SetProperty(ref lyrics, value); }
    }

    private async Task PlayNextAsync()
    {
      StopUpdateLyricTimer();
      musicPlayerService.PlayNext();
      logger.LogInformation("Play next music: {MusicTitle}", musicPlayerService.CurrentMusic?.Title);
      await Task.Delay(1);
    }

    #region 加载和滚动歌词
    private void StopUpdateLyricTimer()
    {
      if (updateLyricTimer.IsEnabled)
      {
        updateLyricTimer.Stop();
      }
    }
    private void StartUpdateLyricTimer()
    {
      if (updateLyricTimer.IsEnabled)
      {
        return;
      }
      updateLyricTimer.Start();
    }
    private async Task UpdateLyric()
    {
      Lyrics = null;
      if (!string.IsNullOrEmpty(musicPlayerService.CurrentMusic.LyricPath))
      {
        Lyrics = await GetLocalLyric();
      }
      else
      {
        Lyrics = await GetInlineLyric();
      }
    }

    readonly Regex pattern = new Regex(@"\[(\d+):(\d+\.\d+)\](.*)");
    private static readonly string LyricFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", "Lyrics");
    /// <summary>
    /// 读取本地歌词
    /// </summary>
    /// <returns></returns>
    private async Task<List<LrcLine>> GetLocalLyric()
    {
      logger.LogInformation("Loading local lyrics for music: {MusicTitle}", musicPlayerService.CurrentMusic?.Title);
      var path = musicPlayerService.CurrentMusic.LyricPath;
      if (path == null)
      {
        return null;
      }
      var lines = new List<LrcLine>();
      foreach (var line in await File.ReadAllLinesAsync(path))
      {
        var one = AnalyzeLyrics(line);
        if (one != null)
        {
          lines.Add(one);
        }
      }
      if (lines != null && lines.Count > 0)
      {
        lines[0].IsFrist = true;
        lines[0].FirstOneMargin = new System.Windows.Thickness(0, ListBoxHeight / 2, 0, 0);
      }
      if (lines != null && lines.Count > 1)
      {
        lines[^1].IsLast = true;
        lines[^1].LastMargin = new System.Windows.Thickness(0, 0, 0, ListBoxHeight / 2);

      }
      //return lines.OrderBy(l => l.Time).ToList();
      return lines;
    }
    private double _ListBoxHeight;

    public double ListBoxHeight
    {
      get { return _ListBoxHeight; }
      set { _ListBoxHeight = value; }
    }

    private LrcLine AnalyzeLyrics(string line)
    {
      var match = pattern.Match(line);
      if (match.Success)
      {
        int minutes = int.Parse(match.Groups[1].Value);
        double seconds = double.Parse(match.Groups[2].Value);
        string text = match.Groups[3].Value.Trim();
        var time = TimeSpan.FromMinutes(minutes) + TimeSpan.FromSeconds(seconds);
        return new LrcLine { Time = time, Text = text };
      }
      return null;
    }

    private async Task<List<LrcLine>> GetInlineLyric()
    {
      logger.LogInformation("Fetching inline lyrics for music: {MusicTitle}", musicPlayerService.CurrentMusic?.Title);
      var songName = musicPlayerService.CurrentMusic.Title;
      var artistName = musicPlayerService.CurrentMusic.Author;
      string keywords = $"{songName} {artistName}";
      string url = $"{ServeUrl}/search?keywords={Uri.EscapeDataString(keywords)}";
      var json = await http.SendJsonRequestAsync(url);
      if (string.IsNullOrEmpty(json))
      {
        return null;
      }
      var jsonObj = JsonSerializer.Deserialize<JsonElement>(json);

      var songId = jsonObj.GetProperty("result")
                          .GetProperty("songs")[0]
                          .GetProperty("id")
                          .GetInt64();
      string? lyricsRes = await http.SendJsonRequestAsync(ServeUrl + "/lyric?id=" + songId);
      if (lyricsRes == null)
      {
        return null;
      }
      var lyricObj = JsonSerializer.Deserialize<JsonElement>(lyricsRes);
      string lyrics = lyricObj.GetProperty("lrc").GetProperty("lyric").GetString();

      Directory.CreateDirectory(LyricFolder);
      string path = Path.Combine(LyricFolder, $"{musicPlayerService.CurrentMusic.ID}.lrc");
      await File.WriteAllTextAsync(path, lyrics);
      musicPlayerService.CurrentMusic.LyricPath = path;
      musicPlayerService.SetSessionDirty();
      logger.LogInformation("");
      var list = lyrics.Split("\n");

      var lines = new List<LrcLine>();
      foreach (var item in list)
      {
        var one = AnalyzeLyrics(item);
        if (one != null)
        {
          lines.Add(one);
        }
      }
      if (lines != null && lines.Count > 0)
      {
        lines[0].IsFrist = true;
      }
      if (lines != null && lines.Count > 1)
      {
        lines[^1].IsLast = true;
      }
      return lines;
    }
    private readonly DispatcherTimer updateLyricTimer;
    private void UpdateLyricTimer_Tick(object sender, EventArgs e)
    {
      if (Lyrics == null || Lyrics.Count == 0)
      {
        return;
      }
      var position = musicPlayerService.CurrentPositionRealTime;
      var currentLine = Lyrics.Where(l => l.Time <= position).LastOrDefault();
      if (currentLine == null)
      {
        return;
      }
      var index = Lyrics.FindIndex(a => a.Time == currentLine.Time);
      if (index == -1 || Lyrics[index].Text == "" || Lyrics[index].IsCurrent)
      {
        return;
      }
      Lyrics.ForEach(l =>
      {
        if (l.IsCurrent)
        {
          l.IsCurrent = false;
        }
      });
      Lyrics[index].IsCurrent = true;
      eventAggregator.GetEvent<ScrollToCenterOfViewEvent>().Publish(index);
    }
    #endregion


    private void PlayMusic()
    {
      if (musicPlayerService.CurrentMusic != null)
      {
        if (musicPlayerService.PlayState == PlayState.Playing)
        {
          musicPlayerService.Pause();
          StopUpdateLyricTimer();
        }
        else
        {
          musicPlayerService.Play();
          StartUpdateLyricTimer();
          DisplayTime = MusicPlayerService.CurrentPosition;
        }
      }
    }

    private void Timer_Tick(object sender, EventArgs e)
    {
      DisplayTime = MusicPlayerService.CurrentPosition;
      if (!_isDragging)
      {
        SliderPosition = MusicPlayerService.CurrentSeconds;
      }
      //Debug.WriteLine(IsDragging);
    }

    readonly DispatcherTimer timer;
    private double _position;
    /// <summary>
    /// 滑块位置，表示当前音乐播放进度（秒）
    /// </summary>
    public double SliderPosition
    {
      get => _position;
      set
      {
        SetProperty(ref _position, value);
      }
    }

    private TimeSpan _displayTime;
    /// <summary>
    /// 显示的时间，用于显示在UI上
    /// </summary>
    public TimeSpan DisplayTime
    {
      get { return _displayTime; }
      set { _displayTime = value; RaisePropertyChanged(); }
    }


    private void MusicProgressChanged(double? time)
    {
      if (time.HasValue)
      {
        MusicPlayerService.CurrentSeconds = time.Value;
        DisplayTime = TimeSpan.FromSeconds(time.Value);
      }

    }

    private void Cancel()
    {
      var journal = RegionManager.Regions[RegionNames.ContentRegion].NavigationService.Journal;
      if (journal.CanGoBack)
      {
        journal.GoBack();
      }
      else
      {
        RegionManager.RequestNavigate(RegionNames.ContentRegion, App_My_ViewNames.APP_MyView);
      }
    }

    public DelegateCommand CancelCommand { get; }
    public DelegateCommand<double?> SeekCommand { get; }

    private bool _isDragging = false;


    public bool IsDragging
    {
      get => _isDragging;
      set
      {
        SetProperty(ref _isDragging, value);
      }
    }

    public override void OnNavigatedTo(NavigationContext navigationContext)
    {
      publishCoreEvent.PublishIsFloatingVisibleEvent(false);
    }
    public override void OnNavigatedFrom(NavigationContext navigationContext)
    {
      publishCoreEvent.PublishIsFloatingVisibleEvent(true);
    }
  }
}
